/*! ******************************************************************************
 *
 * Pentaho Data Integration
 *
 * Copyright (C) 2002-2017 by Hitachi Vantara : http://www.pentaho.com
 *
 *******************************************************************************
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 ******************************************************************************/

package org.pentaho.di.trans.steps.autodoc;

import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;

import javax.imageio.ImageIO;

import org.apache.commons.vfs2.FileObject;
import org.pentaho.di.core.ResultFile;
import org.pentaho.di.core.exception.KettleException;
import org.pentaho.di.core.gui.AreaOwner;
import org.pentaho.di.core.row.RowDataUtil;
import org.pentaho.di.core.vfs.KettleVFS;
import org.pentaho.di.i18n.BaseMessages;
import org.pentaho.di.job.JobMeta;
import org.pentaho.di.repository.RepositoryDirectory;
import org.pentaho.di.repository.RepositoryDirectoryInterface;
import org.pentaho.di.repository.RepositoryObjectType;
import org.pentaho.di.trans.Trans;
import org.pentaho.di.trans.TransMeta;
import org.pentaho.di.trans.step.BaseStep;
import org.pentaho.di.trans.step.StepDataInterface;
import org.pentaho.di.trans.step.StepInterface;
import org.pentaho.di.trans.step.StepMeta;
import org.pentaho.di.trans.step.StepMetaInterface;
import org.pentaho.di.trans.steps.autodoc.KettleReportBuilder.OutputType;
import org.pentaho.reporting.engine.classic.core.ClassicEngineBoot;
import org.pentaho.reporting.libraries.base.util.ObjectUtilities;
import org.pentaho.reporting.libraries.fonts.LibFontBoot;
import org.pentaho.reporting.libraries.resourceloader.LibLoaderBoot;

/**
 * Reads a set of transformation and job filenames and documents those.
 *
 * @author Matt
 * @since 2010-mar-14
 */
public class AutoDoc extends BaseStep implements StepInterface {
  private static Class<?> PKG = AutoDoc.class; // for i18n purposes, needed by Translator2!!

  private AutoDocMeta meta;
  private AutoDocData data;

  public AutoDoc( StepMeta stepMeta, StepDataInterface stepDataInterface, int copyNr, TransMeta transMeta,
    Trans trans ) {
    super( stepMeta, stepDataInterface, copyNr, transMeta, trans );
  }

  public boolean processRow( StepMetaInterface smi, StepDataInterface sdi ) throws KettleException {
    meta = (AutoDocMeta) smi;
    data = (AutoDocData) sdi;

    Object[] row = getRow();
    if ( row == null ) {

      if ( data.filenames.isEmpty() ) {
        // Nothing to see here, move along!
        //
        setOutputDone();
        return false;
      }

      // End of the line, create the documentation...
      //
      FileObject targetFile = KettleVFS.getFileObject( environmentSubstitute( meta.getTargetFilename() ) );
      String targetFilename = KettleVFS.getFilename( targetFile );

      // Create the report builder
      //
      KettleReportBuilder kettleReportBuilder =
        new KettleReportBuilder( this, data.filenames, KettleVFS.getFilename( targetFile ), meta );

      try {
        // Try to get the Classic Reporting Engine to boot inside of the plugin class loader...
        //
        if ( ClassicEngineBoot.getInstance().isBootDone() == false ) {

          ObjectUtilities.setClassLoader( getClass().getClassLoader() );
          ObjectUtilities.setClassLoaderSource( ObjectUtilities.CLASS_CONTEXT );

          LibLoaderBoot.getInstance().start();
          LibFontBoot.getInstance().start();
          ClassicEngineBoot.getInstance().start();
        }

        // Do the reporting thing...
        //
        kettleReportBuilder.createReport();
        kettleReportBuilder.render();

        Object[] outputRowData = RowDataUtil.allocateRowData( data.outputRowMeta.size() );
        int outputIndex = 0;
        outputRowData[outputIndex++] = targetFilename;

        // Pass along the data to the next steps...
        //
        putRow( data.outputRowMeta, outputRowData );

        // Add the target file to the result file list
        //
        ResultFile resultFile =
          new ResultFile( ResultFile.FILE_TYPE_GENERAL, targetFile, getTransMeta().getName(), toString() );
        resultFile.setComment( "This file was generated by the 'Auto Documentation Output' step" );
        addResultFile( resultFile );
      } catch ( Exception e ) {
        throw new KettleException( BaseMessages.getString( PKG, "AutoDoc.Exception.UnableToRenderReport" ), e );
      }

      setOutputDone();
      return false;
    }

    if ( first ) {
      first = false;

      data.outputRowMeta = getInputRowMeta().clone();
      meta.getFields( data.outputRowMeta, getStepname(), null, null, this, repository, metaStore );

      // Get the filename field index...
      //
      String filenameField = environmentSubstitute( meta.getFilenameField() );
      data.fileNameFieldIndex = getInputRowMeta().indexOfValue( filenameField );
      if ( data.fileNameFieldIndex < 0 ) {
        throw new KettleException( BaseMessages.getString(
          PKG, "AutoDoc.Exception.FilenameFieldNotFound", filenameField ) );
      }

      // Get the file type field index...
      //
      String fileTypeField = environmentSubstitute( meta.getFileTypeField() );
      data.fileTypeFieldIndex = getInputRowMeta().indexOfValue( fileTypeField );
      if ( data.fileTypeFieldIndex < 0 ) {
        throw new KettleException( BaseMessages.getString(
          PKG, "AutoDoc.Exception.FileTypeFieldNotFound", fileTypeField ) );
      }

      data.repository = getTrans().getRepository();
      if ( data.repository != null ) {
        data.tree = data.repository.loadRepositoryDirectoryTree();
      }

      // Initialize the repository information handlers (images, metadata, loading, etc)
      //
      TransformationInformation.init( getTrans().getRepository() );
      JobInformation.init( getTrans().getRepository() );
    }

    // One more transformation or job to place in the documentation.
    //
    String fileName = getInputRowMeta().getString( row, data.fileNameFieldIndex );
    String fileType = getInputRowMeta().getString( row, data.fileTypeFieldIndex );

    RepositoryObjectType objectType;
    if ( "Transformation".equalsIgnoreCase( fileType ) ) {
      objectType = RepositoryObjectType.TRANSFORMATION;
    } else if ( "Job".equalsIgnoreCase( fileType ) ) {
      objectType = RepositoryObjectType.JOB;
    } else {
      throw new KettleException( BaseMessages.getString( PKG, "AutoDoc.Exception.UnknownFileTypeValue", fileType ) );
    }

    ReportSubjectLocation location = null;
    if ( getTrans().getRepository() == null ) {
      switch ( objectType ) {
        case TRANSFORMATION:
          location = new ReportSubjectLocation( fileName, null, null, objectType );
          break;
        case JOB:
          location = new ReportSubjectLocation( fileName, null, null, objectType );
          break;
        default:
          break;
      }
    } else {
      int lastSlashIndex = fileName.lastIndexOf( RepositoryDirectory.DIRECTORY_SEPARATOR );
      if ( lastSlashIndex < 0 ) {
        fileName = RepositoryDirectory.DIRECTORY_SEPARATOR + fileName;
        lastSlashIndex = 0;
      }

      String directoryName = fileName.substring( 0, lastSlashIndex + 1 );
      String objectName = fileName.substring( lastSlashIndex + 1 );

      RepositoryDirectoryInterface directory = data.tree.findDirectory( directoryName );
      if ( directory == null ) {
        throw new KettleException( BaseMessages.getString(
          PKG, "AutoDoc.Exception.RepositoryDirectoryNotFound", directoryName ) );
      }

      location = new ReportSubjectLocation( null, directory, objectName, objectType );
    }

    if ( location == null ) {
      throw new KettleException( BaseMessages.getString(
        PKG, "AutoDoc.Exception.UnableToDetermineLocation", fileName, fileType ) );
    }

    if ( meta.getOutputType() != OutputType.METADATA ) {
      // Add the file location to the list for later processing in one output report
      //
      data.filenames.add( location );
    } else {
      // Load the metadata from the transformation / job...
      // Output it in one row for each input row
      //
      Object[] outputRow = RowDataUtil.resizeArray( row, data.outputRowMeta.size() );
      int outputIndex = getInputRowMeta().size();

      List<AreaOwner> imageAreaList = null;

      switch ( location.getObjectType() ) {
        case TRANSFORMATION:
          TransformationInformation ti = TransformationInformation.getInstance();
          TransMeta transMeta = ti.getTransMeta( location );
          imageAreaList = ti.getImageAreaList( location );

          // TransMeta
          outputRow[outputIndex++] = transMeta;
          break;

        case JOB:
          JobInformation ji = JobInformation.getInstance();
          JobMeta jobMeta = ji.getJobMeta( location );
          imageAreaList = ji.getImageAreaList( location );

          // TransMeta
          outputRow[outputIndex++] = jobMeta;
          break;
        default:
          break;
      }

      // Name
      if ( meta.isIncludingName() ) {
        outputRow[outputIndex++] = KettleFileTableModel.getName( location );
      }

      // Description
      if ( meta.isIncludingDescription() ) {
        outputRow[outputIndex++] = KettleFileTableModel.getDescription( location );
      }

      // Extended Description
      if ( meta.isIncludingExtendedDescription() ) {
        outputRow[outputIndex++] = KettleFileTableModel.getExtendedDescription( location );
      }

      // created
      if ( meta.isIncludingCreated() ) {
        outputRow[outputIndex++] = KettleFileTableModel.getCreation( location );
      }

      // modified
      if ( meta.isIncludingModified() ) {
        outputRow[outputIndex++] = KettleFileTableModel.getModification( location );
      }

      // image
      if ( meta.isIncludingImage() ) {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        try {
          BufferedImage image = KettleFileTableModel.getImage( location );
          ImageIO.write( image, "png", outputStream );

          outputRow[outputIndex++] = outputStream.toByteArray();
        } catch ( Exception e ) {
          throw new KettleException( "Unable to serialize image to PNG", e );
        } finally {
          try {
            outputStream.close();
          } catch ( IOException e ) {
            throw new KettleException( "Unable to serialize image to PNG", e );
          }
        }
      }

      if ( meta.isIncludingLoggingConfiguration() ) {
        outputRow[outputIndex++] = KettleFileTableModel.getLogging( location );
      }

      if ( meta.isIncludingLastExecutionResult() ) {
        outputRow[outputIndex++] = KettleFileTableModel.getLogging( location );
      }

      if ( meta.isIncludingImageAreaList() ) {
        outputRow[outputIndex++] = imageAreaList;
      }

      putRow( data.outputRowMeta, outputRow );
    }

    return true;
  }
}
